package utils

import (
	"context"
	"encoding/json"
	"errors"
	. "fastdp-ops/pkg/server/api/middleware"
	. "fastdp-ops/pkg/server/config"
	. "fastdp-ops/pkg/server/grpc"
	pb "fastdp-ops/proto/machine"
	"fmt"
	"sync"
	"time"

	"fastdp-ops/pkg/server/api/models"
)

// SyncMachineEnv 同步所有机器的环境信息到数据库（无HTTP响应，仅更新数据库并返回错误）
// 功能：查询所有机器环境信息，更新在线状态和硬件信息到数据库，失败返回汇总错误
func SyncMachineEnv() error {
	// 1. 从数据库查询所有机器列表
	machineList := []models.Machines{}
	if err := DB.Find(&machineList).Error; err != nil {
		return fmt.Errorf("查询数据库机器列表失败: %w", err)
	}
	if len(machineList) == 0 {
		return errors.New("数据库中无机器记录")
	}

	// 2. 准备并发资源
	agentPort := V.GetString("fastdp-agent.port")
	if agentPort == "" {
		agentPort = "8700" // 默认端口兜底
	}
	var (
		wg        sync.WaitGroup
		mu        sync.Mutex // 保护错误列表并发安全
		allErrors []error    // 收集所有机器的处理错误
	)

	// 3. 并发处理每台机器
	for _, machine := range machineList {
		wg.Add(1)
		go func(machine models.Machines) {
			defer wg.Done()
			var machineErrors []error // 单台机器的错误列表

			// 3.1 先从数据库查询最新记录（避免脏读）
			var dbMachine models.Machines
			if err := DB.Where("machine_ip = ?", machine.MachineIP).First(&dbMachine).Error; err != nil {
				machineErrors = append(machineErrors, fmt.Errorf("主机 %s 数据库原数据查询失败: %w", machine.MachineIP, err))
				mu.Lock()
				allErrors = append(allErrors, machineErrors...)
				mu.Unlock()
				return
			}

			// 3.2 尝试连接Agent获取环境信息
			addr := machine.MachineIP + ":" + agentPort
			conn, err := GetClientConn(addr)
			if err != nil {
				// 连接失败：标记为离线，更新数据库状态
				machineErrors = append(machineErrors, fmt.Errorf("主机 %s 连接Agent失败: %w", machine.MachineIP, err))

				// 更新状态为离线（0）
				if updateErr := DB.Model(&models.Machines{}).
					Where("machine_ip = ?", machine.MachineIP).
					Update("status", 0).Error; updateErr != nil {
					machineErrors = append(machineErrors, fmt.Errorf("主机 %s 更新离线状态失败: %w", machine.MachineIP, updateErr))
				}

				mu.Lock()
				allErrors = append(allErrors, machineErrors...)
				mu.Unlock()
				return
			}
			defer conn.Close() // 确保连接关闭

			// 3.3 调用RPC获取环境信息
			client := pb.NewMachineEnvServiceClient(conn)
			ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
			defer cancel()

			resp, err := client.GetMachineEnv(ctx, &pb.EnvRequest{
				MachineId: machine.MachineIP,
				InfoTypes: []string{}, // 获取所有类型信息
			})
			if err != nil {
				// RPC调用失败：标记为离线
				machineErrors = append(machineErrors, fmt.Errorf("主机 %s RPC调用失败: %w", machine.MachineIP, err))

				// 更新状态为离线
				if updateErr := DB.Model(&models.Machines{}).
					Where("machine_ip = ?", machine.MachineIP).
					Update("status", 0).Error; updateErr != nil {
					machineErrors = append(machineErrors, fmt.Errorf("主机 %s 更新离线状态失败: %w", machine.MachineIP, updateErr))
				}

				mu.Lock()
				allErrors = append(allErrors, machineErrors...)
				mu.Unlock()
				return
			}

			// 3.4 RPC成功：更新数据库环境信息和在线状态
			if resp.EnvInfo == nil {
				machineErrors = append(machineErrors, fmt.Errorf("主机 %s 未返回环境信息", machine.MachineIP))
				mu.Lock()
				allErrors = append(allErrors, machineErrors...)
				mu.Unlock()
				return
			}

			// 序列化磁盘信息（适配数据库JSON字段）
			disksJSON, err := json.Marshal(resp.EnvInfo.Disks)
			if err != nil {
				machineErrors = append(machineErrors, fmt.Errorf("主机 %s 磁盘信息序列化失败: %w", machine.MachineIP, err))
				// 序列化失败仍继续更新其他字段，不中断
			}

			// 构造更新数据
			updateData := models.Machines{
				Hostname:      resp.EnvInfo.Hostname,
				OSName:        resp.EnvInfo.Os.Name,
				KernelVersion: resp.EnvInfo.Os.Kernel,
				Arch:          resp.EnvInfo.Os.Arch,
				Uptime:        resp.EnvInfo.Uptime,
				CPUName:       resp.EnvInfo.Cpu.Model,
				CPUCores:      int(resp.EnvInfo.Cpu.Cores),
				MemoryTotal:   int(resp.EnvInfo.Memory.TotalMb),
				MemoryFree:    int(resp.EnvInfo.Memory.FreeMb),
				Disks:         json.RawMessage(disksJSON),
				Status:        1, // 标记为在线
			}

			// 执行更新
			if err := DB.Model(&models.Machines{}).
				Where("machine_ip = ?", machine.MachineIP).
				Updates(updateData).Error; err != nil {
				machineErrors = append(machineErrors, fmt.Errorf("主机 %s 更新环境信息失败: %w", machine.MachineIP, err))
			}

			// 收集单台机器的错误
			if len(machineErrors) > 0 {
				mu.Lock()
				allErrors = append(allErrors, machineErrors...)
				mu.Unlock()
			}
		}(machine)
	}

	// 等待所有机器处理完成
	wg.Wait()

	// 汇总所有错误（无错误返回nil）
	if len(allErrors) > 0 {
		return errors.Join(allErrors...)
	}
	return nil
}
